import { isArray, isNil } from 'lodash-es'
import { reactive, ref, Ref, UnwrapRef } from 'vue'
import { addFormat, appConfig, BaseType, DictProps, IDict } from '../index';


export type DictClassName = keyof IDict | string

const data: Record<string, Ref<DictProps[]>> = {}
const dataById: Record<string, Record<BaseType, DictProps>> = {}
const dataByValue: Record<string, Record<BaseType, DictProps>> = {}
const dataApi: Record<string, (params?: any) => Promise<DictProps[]> | DictProps[]> = {}

async function listDictsByClass(clazz: string, params?: any): Promise<Ref<UnwrapRef<DictProps[]>>> {
  let arr: DictProps[]
  try {
    const api = dataApi[clazz]
    if (api) {
      arr = await api(params)
    } else {
      arr = await appConfig.dict.listDictsByClass(clazz, params)
    }
  } catch {
    arr = []
  }

  data[clazz].value = arr
  for (const item of arr) {
    if (!isNil(item.id)) {
      dataById[clazz][item.id] = item
    }
    if (!isNil(item.value)) {
      dataByValue[clazz][item.value] = item
    }
  }
  return data[clazz]
}

export function listDicts(clazz: DictClassName, params?: any, cache: boolean = appConfig.dict.cache): Ref<UnwrapRef<DictProps[]>> {
  const out = data[clazz]
  if (out?.value) {
    cache || listDictsByClass(clazz as string, params)
    return out
  } else {
    data[clazz] = ref([])
    dataById[clazz] = reactive({})
    dataByValue[clazz] = reactive({})
    listDictsByClass(clazz as string, params)
    return data[clazz]
  }
}

export function getDictById(id: number | string, clazz: DictClassName, cache?: boolean): DictProps {
  const list = data[clazz]
  if (list?.value) {
    cache || listDicts(clazz)
  } else {
    listDicts(clazz)
  }
  return dataById[clazz][id]
}

export function getDictByValue(val: any, clazz: DictClassName, cache?: boolean): DictProps {
  const list = data[clazz]
  if (list?.value) {
    cache || listDicts(clazz)
  } else {
    listDicts(clazz)
  }
  return dataByValue[clazz][val]
}

export function getDict(val: any, clazz: DictClassName, cache?: boolean): DictProps {
  if (appConfig.dict.valueKey === 'value') {
    return getDictByValue(val, clazz, cache)
  } else {
    return getDictById(val, clazz, cache)
  }
}

export function addDict(clazz: string, fn: (params?: any) => Promise<DictProps[]> | DictProps[]): void {
  dataApi[clazz] = fn
}

addFormat('DictValue', (val: any, clazz: string | [string, boolean]) => {
  if (isArray(clazz)) {
    if (!clazz[0] || !clazz[1]) {
      console.warn('参数错误[clazz, cache]')
      return ''
    } else {
      return getDictByValue(val, clazz[0], clazz[1])?.name ?? '';
    }
  } else {
    return getDictByValue(val, <never>clazz)?.name ?? '';
  }
})

addFormat('DictId', (id: number | string, clazz: string | [string, boolean]) => {
  if (isArray(clazz)) {
    if (!clazz[0] || !clazz[1]) {
      console.warn('参数错误[clazz, cache]')
      return ''
    } else {
      return getDictById(id, clazz[0], clazz[1])?.name ?? '';
    }
  } else {
    return getDictById(id, <never>clazz)?.name ?? '';
  }
})

addFormat('Dict', (val: any, clazz: string | [string, boolean]) => {
  if (isArray(clazz)) {
    if (!clazz[0] || !clazz[1]) {
      console.warn('参数错误[clazz, cache]')
      return ''
    } else {
      return getDict(val, clazz[0], clazz[1])?.name ?? '';
    }
  } else {
    return getDict(val, <never>clazz, true)?.name ?? '';
  }
})

declare module '../index' {
  interface IFormat {
    Dict: (val: any, clazz: string) => string
    DictId: (id: number | string, clazz: string) => string
    DictValue: (val: any, clazz: string) => string
  }
}
